{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "59e4535f",
   "metadata": {},
   "source": [
    "## Interactive Polarization Ellipse\n",
    "\n",
    "This notebook plots the ellipse drawn by the two-dimensional electric field vector of a monochromatic electromagnetic wave, demonstrating the relation between the real-valued Cartesian coordinates of the electric field and the complex-valued analytic representation of those coordinates (as a function of time).  Use the slider bars to adjust the amplitude, orientation and ellipticity of the polarization ellipse.  \n",
    "\n",
    "The following block of code creates the objects (lines and arrows) that will be used to plot the ellipses, configures the plot axes, and creates the slider bars."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c6654a86",
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy import linspace, exp, cos, sin, pi, real\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.animation as animation\n",
    "from ipywidgets import interactive_output, FloatSlider, HBox, VBox, Layout\n",
    "from IPython.display import display\n",
    "        \n",
    "# Set up the figure and the objects to hold plot data\n",
    "fig = plt.figure(figsize=(8,8))\n",
    "    \n",
    "real_ellipse, = plt.plot([], [], color='k', linewidth=2, label=r\"Equation 4\")\n",
    "comp_ellipse, = plt.plot([], [], color='r', linestyle='dotted', linewidth=2, label=r\"Equation 20\")\n",
    "\n",
    "hw, hl = 0.06, 0.10\n",
    "real_arrow = plt.arrow([],[],[],[], head_width=hw, head_length=hl, fc='black', ec='black')\n",
    "comp_arrow = plt.arrow([],[],[],[], head_width=hw, head_length=hl, fc='red', ec='red')\n",
    "\n",
    "# Set up the figure axes, etc.\n",
    "plt.xlim(-1.0, 1.0)\n",
    "plt.ylim(-1.0, 1.0)\n",
    "plt.legend(loc=\"upper left\")\n",
    "    \n",
    "# Place the figure spines at the origin\n",
    "ax = plt.gca()\n",
    "ax.spines['right'].set_color('none')\n",
    "ax.spines['top'].set_color('none')\n",
    "ax.xaxis.set_ticks_position('bottom')\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.yaxis.set_ticks_position('left')\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "# Turn off tick labels\n",
    "ax.set_yticklabels([])\n",
    "ax.set_xticklabels([])\n",
    "    \n",
    "plt.close()\n",
    "\n",
    "# Create slider bars\n",
    "r_val   = FloatSlider(min=0, max=1.0, step=0.1, value=1.0, description=\"$r$\")\n",
    "chi_deg = FloatSlider(min=-45, max=45, step=9, value=25.0, description=\"$\\chi$ (deg)\")\n",
    "psi_deg = FloatSlider(min=-90, max=90, step=9, value=45.0, description=\"$\\psi$ (deg)\")\n",
    "\n",
    "# Stack sliders vertically\n",
    "box_layout = Layout(display='flex',\n",
    "                    flex_flow='column',\n",
    "                    align_items='center',\n",
    "                    justify_content='center',\n",
    "                    border='none',\n",
    "                    width='50%')\n",
    "\n",
    "ui = VBox([r_val, chi_deg, psi_deg], layout=box_layout)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1aa9675",
   "metadata": {},
   "source": [
    "To make the phase of the complex-valued analytic representation of the electromagnetic match that of the real-valued wave described by Equation (2) of Stokes (1852), it is necessary to delay the analytic signal by $\\pi/2$ radians of phase, which is equivalent to multiplying by $-j$.  Changing the absolute phase of the signal has no impact on polarization state and is not strictly necessary; however, in this simulation, matching the phases of the real-valued and complex-valued waves helps to predict where the arrows will be drawn.\n",
    "\n",
    "To understand the need for the $-90$ degree phase shift, consider the primed coordinate system, where $\\psi = 0$. Equation (2) of Stokes (1852) and Equation (4) of van Straten (2024) describe the real-valued components of the electric field vector in this frame of reference,\n",
    "\n",
    "${\\bf\\epsilon}^\\prime(t) =\n",
    "        \\left(\n",
    "        \\begin{array}{c}\n",
    "                \\epsilon_x^\\prime(t) \\\\\n",
    "                \\epsilon_y^\\prime(t)\n",
    "        \\end{array}\n",
    "        \\right) =\n",
    "        \\left(\n",
    "        \\begin{array}{c}\n",
    "                r \\cos\\chi \\sin (2\\pi \\nu t) \\\\\n",
    "                r \\sin\\chi \\cos (2\\pi \\nu t)\n",
    "        \\end{array}\n",
    "        \\right),$\n",
    "        \n",
    "where, without any loss of generality, the absolute phase $\\phi$ is set to zero. Similarly, based on Equation (10) of van Straten (2024), the analytic signal associated with ${\\bf\\epsilon}^\\prime(t)$ is given by\n",
    "\n",
    "${\\bf e}^\\prime(t)=\n",
    "  r\\left( \\begin{array}{c}\n",
    "    \\cos\\chi \\\\\n",
    "    j\\sin\\chi\n",
    "  \\end{array} \\right) \\exp (2\\pi \\nu t + \\delta),$\n",
    "\n",
    "where $\\delta$ is the phase difference between the real-valued and complex-valued representations of the signal.  The real part,\n",
    "\n",
    "$\\mathcal{R}\\left[{\\bf e}^\\prime(t)\\right] =r\\left( \\begin{array}{c}\n",
    "    \\cos\\chi\\cos (2\\pi \\nu t + \\delta)  \\\\\n",
    "   -\\sin\\chi\\sin (2\\pi \\nu t + \\delta)\n",
    "  \\end{array} \\right)$,\n",
    "  \n",
    "and setting ${\\bf\\epsilon}^\\prime(t) = \\mathcal{R}\\left[{\\bf e}^\\prime(t)\\right]$ yields $\\delta = -\\pi/2$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aea53f10",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "87514e23226c4aeaaf625fbe86f209e3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(Output(), VBox(children=(FloatSlider(value=1.0, description='$r$', max=1.0), FloatSlider(value=…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 60 steps around ellipse (+1 because 0 and 2pi overlap)\n",
    "N = 61\n",
    "phi = linspace(0, 2*pi, N)\n",
    "\n",
    "# Arrays of cosines and sines\n",
    "cosph = cos(phi)\n",
    "sinph = sin(phi)\n",
    "\n",
    "# Array of complex exponential values (-j * see note above)\n",
    "expph = -1j * exp(1j*phi)\n",
    "\n",
    "## Callback computes Equations 4 and 20 and sets data in plot objects\n",
    "def plot_ellipse_comparison(r_val,chi_deg,psi_deg):\n",
    "        \n",
    "    r = r_val\n",
    "    chi = chi_deg*pi/180.0\n",
    "    psi = psi_deg*pi/180.0\n",
    "        \n",
    "    ## Equation 4\n",
    "    xprime = r*cos(chi)*sinph\n",
    "    yprime = r*sin(chi)*cosph\n",
    "        \n",
    "    ## Inverse of Equations 2 and 3\n",
    "    x = xprime*cos(psi) - yprime*sin(psi)\n",
    "    y = xprime*sin(psi) + yprime*cos(psi)\n",
    "        \n",
    "    real_ellipse.set_data(x, y)\n",
    "\n",
    "    # draw real arrow at zero phase\n",
    "    q=N-1\n",
    "    real_arrow.set_data(x=x[q], y=y[q], dx=x[0]-x[q], dy=y[0]-y[q])\n",
    "\n",
    "    ## Equation 20\n",
    "    e_x = r * complex( cos(psi)*cos(chi), -sin(psi)*sin(chi) ) * expph\n",
    "    e_y = r * complex( sin(psi)*cos(chi),  cos(psi)*sin(chi) ) * expph\n",
    "\n",
    "    x = real(e_x)\n",
    "    y = real(e_y)\n",
    "        \n",
    "    comp_ellipse.set_data(x, y)\n",
    "    \n",
    "    # draw complex arrow at 180 deg phase\n",
    "    q=N//2-1\n",
    "    comp_arrow.set_data(x=x[q], y=y[q], dx=x[q+1]-x[q], dy=y[q+1]-y[q])\n",
    "\n",
    "    display(fig)\n",
    "\n",
    "## Attach slider bars to plot_ellipse_comparison callback\n",
    "w = interactive_output(plot_ellipse_comparison,{'r_val':r_val, 'chi_deg':chi_deg, 'psi_deg':psi_deg})\n",
    "    \n",
    "# Horizontal Layout puts sliders beside plot\n",
    "display(HBox([w,ui]))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
